package mongodb

import com.mongodb.MongoCredential._
import helpers._
import org.mongodb.scala._
import org.mongodb.scala.connection.ClusterSettings

import scala.collection.JavaConverters._

class MongodbContext(configuration: Configuration, prefix: String = "mongodb") {
  private final val settings = MongoClientSettings.builder()

  def database   = configuration.getEssentialString(appendable("database"))
  def username   = configuration.getOptional[String](appendable("username")).getOrElse("")
  def password   = configuration.getOptional[String](appendable("password")).getOrElse("")
  def server     = configuration.getOptional[String](appendable("server")).getOrElse("localhost")
  def port       = configuration.getOptional[Int](appendable("port")).getOrElse(27017)
  def servers    = configuration.getOptional[Seq[String]](appendable("servers")).getOrElse(Nil)
  def mechanism  = configuration.getOptional[String](appendable("mechanism"))
  def authSource = configuration.getOptional[String](appendable("authSource")).getOrElse("admin")

  def authentications: MongoCredential = mechanism match {
    case Some("MONGODB-CR") => createMongoCRCredential(username, authSource, password.toCharArray)
    case Some("x.509")      => createMongoX509Credential(username)
    case Some("GSSAPI")     => createGSSAPICredential(username)
    case Some("PLAIN")      => createPlainCredential(username, authSource, "$external".toCharArray)
    case Some("SCRAM-SHA-1") =>
      createScramSha1Credential(username, authSource, password.toCharArray)
    case _ => createCredential(username, authSource, password.toCharArray)
  }

  settings.clusterSettings(
    ClusterSettings.builder().hosts((servers :+ server).map(ServerAddress(_)).asJava).build()
  )
  if (mechanism.isDefined) settings.credential(authentications)

  lazy val client: MongoClient = MongoClient(settings.build())
  lazy val db: MongoDatabase   = client.getDatabase(database)

  def collection(name: String): MongoCollection[Document] = db.getCollection(name)

  def collection(namespace: String, collectionName: String): MongoCollection[Document] =
    client.getDatabase(namespace).getCollection(collectionName)

  def close(): Unit = client.close()

  protected def appendable(part: String): String = if (prefix.isEmpty) part else s"$prefix.$part"
}
